package birdsVSZombiesClient;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;

import com.esotericsoftware.kryonet.Client;
import com.esotericsoftware.kryonet.Connection;
import com.esotericsoftware.kryonet.Listener;
import com.google.gson.Gson;

import ddf.minim.AudioInput;
import ddf.minim.AudioPlayer;
import ddf.minim.Minim;

import birdsVSZombiesClient.GUI.BVZButton;
import birdsVSZombiesClient.GUI.BVZRectButton;
import birdsVSZombiesClient.gameGraphic.BVZGameShape;
import processing.core.PApplet;
import processing.core.PConstants;
import processing.core.PImage;
import processing.core.PShape;
import shared.BVZClientToServerMessages;
import shared.BVZConstants;
import shared.BVZServerToClientsMessages;
import shared.Network;

/**
 *	The Client class where runs the client and redraw the scene according to the data sent back form the server.
 *	@author Yang Liu
 *	@version Version 0.0.4
 */
public class BirdsVSZombiesClient extends PApplet {
	
	//	The kryo version of client
	public Client bvzClient;

	public int clientStat = CONNECTING;
	public int playerRole = BVZConstants.NOROLE;
	
	//	Game Texture;
	public HashMap<String, PImage> gameTexture = new HashMap<String, PImage>(19);
	public PImage background;
	
	//	Client Status
	public static final int CONNECTING = 0;
	public static final int STARTINIT = 1;
	public static final int ENDINIT = 2;
	public static final int STARTPHYSSYNC = 3;
	
	//	Game UI
	public ArrayList<BVZRectButton> UIButtons;
	public BVZRectButton startButton;
	public BVZRectButton shooterButton;
	public BVZRectButton defenderButton;
	public PImage shooterButtonImage;
	public PImage shooterButtonImageHover;
	public PImage defenderButtonImage;
	public PImage defenderButtonImageHover;
	
	public PImage logo;
	
	//	Json messages
	public Gson gson;
	BVZServerToClientsMessages msg;
	
	//	Game Audoi
	public Minim minim;
	public AudioPlayer themeMusic;
	public AudioInput input;
	
	public void setup() {
		size(BVZConstants.GAMEWINDOWWIDTH, BVZConstants.GAMEWINDOWHEIGHT);
		frameRate(BVZConstants.GAMEFRAMERATE);
		
		//	Setup the clients
		bvzClient = new Client();
		bvzClient.start();
		try {
			bvzClient.connect(5000, "127.0.0.1", Network.TCPport, Network.UDPport);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		Network.register(bvzClient);
		
		BVZClientToServerMessages initialConncetion = makeClientMessage(BVZConstants.CLIENTCONNECTED, BVZConstants.NOROLE, BVZConstants.MOUSECLICKED);
		bvzClient.sendTCP(initialConncetion);
		
		bvzClient.addListener(new Listener(){
			public void received(Connection c, Object obj){
				if(obj instanceof BVZServerToClientsMessages){
					msg = (BVZServerToClientsMessages)obj;
																
					switch (msg.ss) {
						//	Action when connected
						case BVZConstants.SERVERWELCOMEMESSAGE:
							callClientInit();
							break;
						//	Action when the server finish physical initialization
						case BVZConstants.SERVERPHYSINITCOMPLETED:
							
							break;
						//	Action when the server is running the physical simulation
						case BVZConstants.SERVERRUNPHYS:
							callClientSyncWithServer();
							break;
						default:
							break;
					}

				}
			}
		});
		
		background(255);
		smooth();
		
		BVZButton.parent = this;
		BVZButton.btMinim = new Minim(this);
		BVZGameShape.parent = this;
		
		//	Load game texture
		//	Texture for blocks
		gameTexture.put(BVZConstants.WOODBLOCKSMALL, loadImage("wbs.png"));
		gameTexture.put(BVZConstants.WOODBLOCKLARGE, loadImage("wbl.png"));
		gameTexture.put(BVZConstants.WOODBLOCKMEDIUM, loadImage("wbm.png"));
		gameTexture.put(BVZConstants.WOODBLOCKFRAME, loadImage("wbf.png"));
		
		gameTexture.put(BVZConstants.STONEBLOCKSMALL, loadImage("sbs.png"));
		gameTexture.put(BVZConstants.STONEBLOCKLARGE, loadImage("sbl.png"));
		gameTexture.put(BVZConstants.STONEBLOCKMEDIUM, loadImage("sbm.png"));
		gameTexture.put(BVZConstants.STONEBLOCKFRAME, loadImage("sbf.png"));
		
		gameTexture.put(BVZConstants.ICEBLOCKSMALL, loadImage("ibs.png"));
		gameTexture.put(BVZConstants.ICEBLOCKLARGE, loadImage("ibl.png"));
		gameTexture.put(BVZConstants.ICEBLOCKMEDIUM, loadImage("ibm.png"));
		gameTexture.put(BVZConstants.ICEBLOCKFRAME, loadImage("ibf.png"));
		
		//	Texture for zombies
		gameTexture.put(BVZConstants.NORMALZOMBIE, loadImage("nzh.png"));
		gameTexture.put(BVZConstants.PUNKZOMBIE, loadImage("pzh.png"));
		gameTexture.put(BVZConstants.JACKSONZOMBIE, loadImage("jzh.png"));
		
		//	Texture for birds
		gameTexture.put(BVZConstants.NORMALBIRD, loadImage("nb.png"));
		gameTexture.put(BVZConstants.WHITEBIRD, loadImage("wbd.png"));
		gameTexture.put(BVZConstants.BLACKBIRD, loadImage("bbd.png"));
		gameTexture.put(BVZConstants.TOUGHBIRD, loadImage("tbd.png"));
		
		//	Texture for background
		background = loadImage("bg2.png");
		logo = loadImage("logo.png");
		
		//	Texture for buttons
		shooterButtonImage = loadImage("shooterbtn.png");
		shooterButtonImageHover = loadImage("shooterhover.png");
		defenderButtonImage = loadImage("defenderbtn.png");
		defenderButtonImageHover = loadImage("defenderhover.png");
		
		gson = new Gson();
		
		//	Load audio
		minim = new Minim(this);
		themeMusic = minim.loadFile("cerebrawl.mp3");
		
	}

	public void draw() {
		background(background);
		clientSelfUpdate();
	}
	
	public void stop(){
		themeMusic.close();
		minim.stop();
		
		BVZButton.btMinim.stop();
		
		super.stop();
	}
	
	/*
	 * 	Change the client status according to the message sent back from the server.
	 * 	Relpaced by kryo listener
	 */
	public void listenToServer(){
//		String dataIn = bvzClient.readStringUntil('*');
//		
//		if(dataIn != null){
//			dataIn = dataIn.substring(0, dataIn.length() -1);			
//			msg = gson.fromJson(dataIn, BVZServerToClientsMessages.class);
//			
//			switch (msg.ss) {
//				//	Action when connected
//				case BVZConstants.SERVERWELCOMEMESSAGE:
//					callClientInit();
//					break;
//				//	Action when the server finish physical initialization
//				case BVZConstants.SERVERPHYSINITCOMPLETED:
//					
//					break;
//				//	Action when the server is running the physical simulation
//				case BVZConstants.SERVERRUNPHYS:
//					callClientSyncWithServer();
//					break;
//				default:
//					break;
//			}
//		}
	}
	
	/**
	 * Start Initialize Client
	 */
	public void callClientInit(){
		clientStat = STARTINIT;
	}
	
	public void callClientSyncWithServer(){
		clientStat = STARTPHYSSYNC;
	}
	
	/**
	 * Change the behavior based on different client status.
	 */
	public void clientSelfUpdate(){
		switch (clientStat) {
			case CONNECTING:
				//	Do something during the connecting process.
				clientConnecting();
				break;
			case STARTINIT:
				//	Init the client.
				clientInit();
				break;
			case ENDINIT:
				//	Start running the client.
				clientStart();
				break;
			case STARTPHYSSYNC:
//				//	Sycn the clients with the server.
				clientSyncWithServer();
				break;
			default:
				break;
		}
	}
	
	/**
	 * The Action during the connection.
	 */
	public void clientConnecting(){
		
	}
	
	/**
	 * Initialize the client.
	 */
	public void clientInit(){		
		UIButtons = new ArrayList<BVZRectButton>();
		startButton = new BVZRectButton("Click to start!", BVZConstants.SHOOTERBUTTONAUDIO, BVZConstants.SHOOTERBUTTONSELECTED);
		shooterButton = new BVZRectButton(341, 290, BVZConstants.SHOOTERBUTTONAUDIO, BVZConstants.SHOOTERBUTTONSELECTED, shooterButtonImage, shooterButtonImageHover);
		defenderButton = new BVZRectButton(752, 290, BVZConstants.DEFENDERBUTTONAUDIO, BVZConstants.DEFENDERBUTTONSELECTED, defenderButtonImage, defenderButtonImageHover);
//		UIButtons.add(startButton);
		UIButtons.add(shooterButton);
		UIButtons.add(defenderButton);
		clientStat = ENDINIT;
		
		themeMusic.loop();
	}
	
	/**
	 * Start running the client. 
	 * Listening to the server/sending input to the server.
	 */
	public void clientStart(){
//		println("Client start!!!!");
		for(int i=0; i<UIButtons.size(); i++){
			UIButtons.get(i).update();
			UIButtons.get(i).display(1);
		}
		
		image(logo, 178, 109, 854, 151);
	}
	
	/**
	 * Sync Clients with Server physics.
	 */
	public void clientSyncWithServer(){
		startButton = null;
				
		for(int i=0; i<msg.mby.size(); i++){
//			msg.boundaries.get(i).display();
		}
		
		for(int i=0; i<msg.mc.size(); i++){
			msg.mc.get(i).si = gameTexture.get(msg.mc.get(i).gst);
			msg.mc.get(i).display();
			if(msg.mc.get(i).lcn > 0){
				println(i + ": " + msg.mc.get(i).lcn);
			}
		}
		
		for(int i=0; i<msg.mb.size(); i++){
			msg.mb.get(i).si = gameTexture.get(msg.mb.get(i).gst);
			msg.mb.get(i).display();
		}

	}
	
	public void mouseClicked(){
		switch (clientStat) {
			case ENDINIT:
				int currentClientStat;
				if(startButton.pressed()){
					this.playerRole = BVZConstants.NOROLE;
					currentClientStat = BVZConstants.CLIENTSTART;
				}
				else if(shooterButton.pressed()){
					this.playerRole = BVZConstants.BIRDSSHOOTER;
					currentClientStat = BVZConstants.CLIENTJOININ;
					shooterButton.buttonSelectedSound.trigger();
				}
				else if(defenderButton.pressed()){
					this.playerRole = BVZConstants.ZOMBIESDEFENDER;
					currentClientStat = BVZConstants.CLIENTJOININ;
					defenderButton.buttonSelectedSound.trigger();
				}
				else{
					break;
				}
				BVZClientToServerMessages ctsMsg = makeClientMessage(currentClientStat, this.playerRole, BVZConstants.MOUSECLICKED);
				bvzClient.sendTCP(ctsMsg);
				break;
	
			default:
				break;
		}
	}
	
	public void mousePressed(){
		switch (clientStat) {
			case STARTPHYSSYNC:
				BVZClientToServerMessages ctsMsg = makeClientMessage(BVZConstants.CLIENTRUNNINGPHYS, this.playerRole, BVZConstants.MOUSEPRESSED);
				bvzClient.sendTCP(ctsMsg);
				break;
	
			default:
				break;
		}
	}
	
	public void mouseDragged(){
		switch (clientStat) {
			case STARTPHYSSYNC:
				BVZClientToServerMessages ctsMsg = makeClientMessage(BVZConstants.CLIENTRUNNINGPHYS, this.playerRole, BVZConstants.MOUSEDRAGGED);
				bvzClient.sendTCP(ctsMsg);
				
				break;
			default:
				break;
		}
	}
	
	public void mouseReleased(){
		switch (clientStat) {
			case STARTPHYSSYNC:
				BVZClientToServerMessages ctsMsg = makeClientMessage(BVZConstants.CLIENTRUNNINGPHYS, this.playerRole, BVZConstants.MOUSERELEASED);
				bvzClient.sendTCP(ctsMsg);
				break;
				
			default:
				break;
		}
	}
	
	/**
	 * Construct the message which will be sent to the server.
	 * @param _clientStat 	The status of the client.
	 * @param _playerRole	The role of the player.
	 * @param _mouseStat 	The status of the mouse. 
	 * @return The Json representation of the message.
	 */
	public BVZClientToServerMessages makeClientMessage(int _clientStat, int _playerRole, int _mouseStat){
		BVZClientToServerMessages msg = new BVZClientToServerMessages(_clientStat, _playerRole, _mouseStat, mouseX, mouseY);
		return msg;
	}
	
	public static void main(String _args[]) {
		PApplet.main(new String[] { birdsVSZombiesClient.BirdsVSZombiesClient.class.getName() });
	}
}









